package protoredis

import (
	"bytes"
	"errors"
	"strconv"
	"strings"
)

type ClientProtocol struct {
	data    []byte
	point   int
	dataLen int
	cmd     string
}

func NewClientProtocol() *ClientProtocol {
	return &ClientProtocol{}
}

func (p *ClientProtocol) skip(count int) error {
	if p.getBytesCount() < count {
		return errors.New("out of index")
	}
	p.point += count
	return nil
}

func (p *ClientProtocol) reset() {
	p.point = 0
}

func (p *ClientProtocol) isEof() bool {
	return p.point >= p.dataLen
}

func (p *ClientProtocol) GetCmd() string {
	return strings.TrimSpace(strings.ToUpper(p.cmd))
}

func (p *ClientProtocol) GetFullCmd() string {
	p.reset()
	offset := p.getNextCRLFOffset()
	if offset == -1 {
		return "Parse \\r\\n error"
	}
	readBytes, err := p.read(offset)
	if err != nil {
		return err.Error()
	}
	p.getArgCount(readBytes)
	p.skip(2)
	var args []string
	for {
		if p.isEof() {
			break
		}
		offset = p.getNextCRLFOffset()
		if offset == -1 {
			break
		}
		readBytes, err := p.read(offset)
		if err != nil {
			return err.Error()
		}
		argLen := p.getArgBytesCount(readBytes)
		p.skip(2)
		// 读取参数值
		readBytes, err = p.read(argLen)
		if err != nil {
			return err.Error()
		}
		arg := string(readBytes)
		args = append(args, arg)
		if p.cmd == "" {
			p.cmd = arg
		}
	}
	return strings.Join(args, " ")
}

func (p *ClientProtocol) getArgCount(data []byte) int {
	v, _ := strconv.Atoi(strings.ReplaceAll(string(data), "*", ""))
	return v
}

// 获取参数值的字节数。
func (p *ClientProtocol) getArgBytesCount(data []byte) int {
	v, _ := strconv.Atoi(strings.ReplaceAll(string(data), "$", ""))
	return v
}

// 获取下一个换行符的位置。(-1 不存在)
func (p *ClientProtocol) getNextCRLFOffset() int {
	return bytes.Index(p.data[p.point:], []byte{0x0d, 0x0a})
}

func (p *ClientProtocol) read(count int) ([]byte, error) {
	if len(p.data[p.point:]) < count {
		return nil, errors.New("out of index")
	}
	defer func() {
		p.skip(count)
	}()
	return p.data[p.point : p.point+count], nil
}

func (p *ClientProtocol) getBytesCount() int {
	if p.isEof() {
		return 0
	}
	return len(p.data[p.point:])
}

// 判断包是否已经完整。( true -完整， false -不完整)
func (p *ClientProtocol) IsFull() bool {
	p.reset()
	offset := p.getNextCRLFOffset()
	if offset == -1 {
		return false
	}
	readData, err := p.read(offset)
	if err != nil {
		return false
	}
	argCount := p.getArgCount(readData)
	err = p.skip(2)
	if err != nil {
		return false
	}
	for i := 0; i < argCount; i++ {
		if p.isEof() {
			return false
		}
		// 解析参数的字节数
		offset = p.getNextCRLFOffset()
		if offset == -1 {
			return false
		}
		readData, err := p.read(offset)
		if err != nil {
			return false
		}
		argLen := p.getArgBytesCount(readData)
		err = p.skip(2)
		if err != nil {
			return false
		}
		_, err = p.read(argLen)
		if err != nil {
			return false
		}
		err = p.skip(2)
		if err != nil {
			return false
		}
	}
	return true
}

// 拼接剩下的包。
func (p *ClientProtocol) AppendData(data []byte) {
	p.data = append(p.data, data...)
	p.dataLen = len(p.data)
}
